package cn.zyl.demo.common.tools;

import cn.zyl.demo.common.enums.CharsetEnum;
import cn.zyl.demo.common.enums.RegexEnum;
import cn.zyl.demo.common.exception.BeansException;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

/** FileTool 工具类说明: 文件工具类
 * @author wsz
 * @date 2020-08-19
 * @version v1.0
 */
public class FileTool {
    private FileTool(){throw new IllegalAccessError("FileTool");}
    /** 跳转上级的前缀符号*/
    public static final String SKIP_FATHER_PREFIX = "..";
    /** 跳转同级的前缀符号*/
    public static final String SKIP_PEER_PREFIX = ".";
    /** 根路径符号，window下是当前盘符根目录；Linux下是系统盘符根目录*/
    public static final String ROOT_PATH = "/";
    public static final String ROOT_PATH_WIN = "\\";

    /**
     * 拼接文件路径
     * @param rootName 当前路径的根路径,可以是：/ \\ . .. a-z A-Z
     * @param names 子路径名拼接，不含分割符
     * @return 拼接好的路径字符串
     * @throws BeansException 异常
     */
    public static String joinPathByName(String rootName,String ...names)throws BeansException{
        BeansException.isTrueThrow(StringTool.isEmpty(rootName), "rootName value is empty.");
        StringBuilder res = new StringBuilder();
        //系统路径分隔符
        String separator = File.separator;
        if(rootName.equals(SKIP_PEER_PREFIX) || rootName.equals(SKIP_FATHER_PREFIX)){
            //根路径符号，window下是当前盘符根目录；Linux下是系统盘符根目录
            res.append(rootName).append(separator);
        }else if(rootName.equals(ROOT_PATH) || rootName.equals(ROOT_PATH_WIN)){
            //根路径符号，window下是当前盘符根目录；Linux下是系统盘符根目录
            res.append(separator);
        //}else if(Character.isUpperCase(rootName) || Character.isLowerCase(rootName)){
        }else if(RegexEnum.CHAR_EN.matches(rootName) && rootName.length() == 1){
            //window下根目录是字母盘符
            res.append(rootName).append(":").append(separator);
        }else {
            throw new BeansException("rootName is wrong character.");
        }
        if(null == names || names.length <= 0){return res.toString();}
        String name;
        for (int i = 0,len = names.length; i < len ; i++) {
            name = names[i];
            if(StringTool.isEmpty(name)){ continue; }
            res.append(name);
            if(i < len - 1){ res.append(separator); }
        }
        return res.toString();
    }

    /**
     * 相对路径 转 绝对路径
     * @param relativePath 相对路径，含文件全名。最好前缀是 . 或者 .. 或者 /（项目根路径）
     * @return 绝对路径
     * @throws BeansException 相对路径为空异常
     */
    public static String relativeToAbsolutelyByPath(String relativePath)throws BeansException{
        return getProjectRootPath(relativePath);
    }
    /**
     * 获取项目根路径
     * @return D:/_project/IDEA-2020-1/sb-j8-mp-jd/common/target/classes/
     */
    public static String getProjectRootPath(){
        return getProjectRootPath(null);
    }
    private static String getProjectRootPath(String filePath){
        String path;
        if(StringTool.isEmpty(filePath)){
            //如果获取的路径存在空格，D:\Program Files\Apache Software Foundation，D:/Program%20Files/Apache%20Software%20Foundation
            path = FileTool.class.getResource("/").getPath();
        }else {
            path = FileTool.class.getResource(filePath).getPath();
        }
        //需要进行转码
        try {
            return URLDecoder.decode(path, CharsetEnum.UTF_8.getName());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return path;
    }
    // TODO: 2020/8/19  工具类优化-增减函数
    //https://www.cnblogs.com/len0031/p/9039804.html
    /**
     * 修改文件的最后访问时间。
     * 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param file 需要修改最后访问时间的文件。
     * @since 1.0
     */
    public static void touch(File file) {
        long currentTime = System.currentTimeMillis();
        if (!file.exists()) {
            System.err.println("file not found:" + file.getName());
            System.err.println("Create a new file:" + file.getName());
            try {
                if (file.createNewFile()) {
                    System.out.println("Succeeded!");
                } else {
                    System.err.println("Create file failed!");
                }
            } catch (IOException e) {
                System.err.println("Create file failed!");
                e.printStackTrace();
            }
        }
        boolean result = file.setLastModified(currentTime);
        if (!result) {
            System.err.println("touch failed: " + file.getName());
        }
    }

    /**
     * 修改文件的最后访问时间。
     * 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param fileName 需要修改最后访问时间的文件的文件名。
     * @since 1.0
     */
    public static void touch(String fileName) {
        File file = new File(fileName);
        touch(file);
    }

    /**
     * 修改文件的最后访问时间。
     * 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param files 需要修改最后访问时间的文件数组。
     * @since 1.0
     */
    public static void touch(File[] files) {
        for (File file : files) { touch(file); }
    }

    /**
     * 修改文件的最后访问时间。
     * 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param fileNames 需要修改最后访问时间的文件名数组。
     * @since 1.0
     */
    public static void touch(String[] fileNames) {
        File[] files = new File[fileNames.length];
        for (int i = 0; i < fileNames.length; i++) {
            files[i] = new File(fileNames[i]);
        }
        touch(files);
    }

    /**
     * 判断指定的文件是否存在。
     *
     * @param fileName 要判断的文件的文件名
     * @return 存在时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean isFileExist(String fileName) {
        return new File(fileName).isFile();
    }

    /**
     * 创建指定的目录。
     * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。
     * <b>注意：可能会在返回false的时候创建部分父目录。</b>
     *
     * @param file 要创建的目录
     * @return 完全创建成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean makeDirectory(File file) {
        File parent = file.getParentFile();
        if (parent != null) { return parent.mkdirs(); }
        return false;
    }

    /**
     * 创建指定的目录。
     * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。
     * <b>注意：可能会在返回false的时候创建部分父目录。</b>
     *
     * @param fileName 要创建的目录的目录名
     * @return 完全创建成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean makeDirectory(String fileName) {
        File file = new File(fileName);
        return makeDirectory(file);
    }

    /**
     * 清空指定目录中的文件。
     * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
     * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     *
     * @param directory 要清空的目录
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false.
     * @since 1.0
     */
    public static boolean emptyDirectory(File directory) {
        File[] entries = directory.listFiles();
        if(null == entries){return true;}
        boolean result = true;
        for (File entry : entries) {
            if (!entry.delete()) {
                result = false;
            }
        }
        return result;
    }

    /**
     * 清空指定目录中的文件。
     * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
     * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     *
     * @param directoryName 要清空的目录的目录名
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean emptyDirectory(String directoryName) {
        File dir = new File(directoryName);
        return emptyDirectory(dir);
    }

    /**
     * 删除指定目录及其中的所有内容。
     *
     * @param dirName 要删除的目录的目录名
     * @return 删除成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean deleteDirectory(String dirName) {
        return deleteDirectory(new File(dirName));
    }

    /**
     * 删除指定目录及其中的所有内容。
     *
     * @param dir 要删除的目录
     * @return 删除成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean deleteDirectory(File dir) {
        if ((dir == null) || !dir.isDirectory()) {
            throw new IllegalArgumentException("Argument " + dir + " is not a directory. ");
        }
        File[] entries = dir.listFiles();
        for (File entry : entries) {
            if (entry.isDirectory()) {
                if (!deleteDirectory(entry)) { return false; }
            } else {
                if (!entry.delete()) { return false; }
            }
        }
        return dir.delete();
    }
    /**
     * 列出目录中的所有内容，包括其子目录中的内容。
     *
     * @param file   要列出的目录
     * @param filter 过滤器
     * @return 目录内容的文件数组。
     * @since 1.0
     */
    public static File[] listAll(File file, javax.swing.filechooser.FileFilter filter) {
        ArrayList<File> list = new ArrayList<>();
        File[] files;
        if (!file.exists() || file.isFile()) { return null; }
        list(list, file, filter);
        files = new File[list.size()];
        list.toArray(files);
        return files;
    }
    /**
     * 将目录中的内容添加到列表。
     *
     * @param list   文件列表
     * @param filter 过滤器
     * @param file   目录
     */
    private static void list(ArrayList<File> list, File file, javax.swing.filechooser.FileFilter filter) {
        if (filter.accept(file)) {
            list.add(file);
            if (file.isFile()) { return; }
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if(null == files){return;}
            for (File value : files) { list(list, value, filter); }
        }
    }

    /**
     * 返回文件的URL地址。
     *
     * @param file 文件
     * @return 文件对应的的URL地址
     * @throws MalformedURLException 抛出这一异常指示出现了错误的URL
     * @since 1.0
     * @deprecated 在实现的时候没有注意到File类本身带一个toURL方法将文件路径转换为URL。
     * 请使用File.toURL方法。
     */
    public static URL getUrl(File file) throws MalformedURLException {
        String fileURL = "file:/" + file.getAbsolutePath();
        URL url = new URL(fileURL);
        return url;
    }
    /**
     * 从文件路径得到文件名。
     *
     * @param filePath 文件的路径，可以是相对路径也可以是绝对路径
     * @return 对应的文件名
     * @since 1.0
     */
    public static String getFileName(String filePath) {
        File file = new File(filePath);
        return file.getName();
    }
    /**
     * 从文件名得到文件绝对路径。
     *
     * @param fileName 文件名
     * @return 对应的文件路径
     * @since 1.0
     */
    public static String getFilePath(String fileName) {
        File file = new File(fileName);
        return file.getAbsolutePath();
    }
    /**
     * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。
     * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，
     * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。
     *
     * @param filePath 转换前的路径
     * @return 转换后的路径
     * @since 1.0
     */
    public static String toUNIXpath(String filePath) {
        return filePath.replace('\\', '/');
    }
    /**
     * 从文件名得到UNIX风格的文件绝对路径。
     *
     * @param fileName 文件名
     * @return 对应的UNIX风格的文件路径
     * @see #toUNIXpath(String filePath) toUNIXpath
     * @since 1.0
     */
    public static String getUNIXFilePath(String fileName) {
        File file = new File(fileName);
        return toUNIXpath(file.getAbsolutePath());
    }

    /**
     * 得到文件的类型。
     * 实际上就是得到文件名中最后一个“.”后面的部分。
     *
     * @param fileName 文件名
     * @return 文件名中的类型部分
     * @since 1.0
     */
    public static String getTypePart(String fileName) {
        int point = fileName.lastIndexOf('.');
        int length = fileName.length();
        if (point == -1 || point == length - 1) {
            return "";
        } else {
            return fileName.substring(point + 1, length);
        }
    }
    /**
     * 得到文件的类型。
     * 实际上就是得到文件名中最后一个“.”后面的部分。
     *
     * @param file 文件
     * @return 文件名中的类型部分
     * @since 1.0
     */
    public static String getFileType(File file) {
        return getTypePart(file.getName());
    }
    /**
     * 得到文件的名字部分。
     * 实际上就是路径中的最后一个路径分隔符后的部分。
     *
     * @param fileName 文件名
     * @return 文件名中的名字部分
     * @since 1.0
     */
    public static String getNamePart(String fileName) {
        int point = getPathLsatIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return fileName;
        } else if (point == length - 1) {
            int secondPoint = getPathLsatIndex(fileName, point - 1);
            if (secondPoint == -1) {
                if (length == 1) {
                    return fileName;
                } else {
                    return fileName.substring(0, point);
                }
            } else {
                return fileName.substring(secondPoint + 1, point);
            }
        } else {
            return fileName.substring(point + 1);
        }
    }
    /**
     * 得到文件名中的父路径部分。
     * 对两种路径分隔符都有效。
     * 不存在时返回""。
     * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。
     *
     * @param fileName 文件名
     * @return 父路径，不存在或者已经是父目录时返回""
     * @since 1.0
     */
    public static String getPathPart(String fileName) {
        int point = getPathLsatIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return "";
        } else if (point == length - 1) {
            int secondPoint = getPathLsatIndex(fileName, point - 1);
            if (secondPoint == -1) {
                return "";
            } else {
                return fileName.substring(0, secondPoint);
            }
        } else {
            return fileName.substring(0, point);
        }
    }

    /**
     * 得到路径分隔符在文件路径中首次出现的位置。
     * 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName 文件路径
     * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathIndex(String fileName) {
        int point = fileName.indexOf('/');
        if (point == -1) {
            point = fileName.indexOf('\\');
        }
        return point;
    }
    /**
     * 得到路径分隔符在文件路径中指定位置后首次出现的位置。
     * 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName  文件路径
     * @param fromIndex 开始查找的位置
     * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathIndex(String fileName, int fromIndex) {
        int point = fileName.indexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.indexOf('\\', fromIndex);
        }
        return point;
    }
    /**
     * 得到路径分隔符在文件路径中最后出现的位置。
     * 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName 文件路径
     * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathLsatIndex(String fileName) {
        int point = fileName.lastIndexOf('/');
        if (point == -1) {
            point = fileName.lastIndexOf('\\');
        }
        return point;
    }
    /**
     * 得到路径分隔符在文件路径中指定位置前最后出现的位置。
     * 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName  文件路径
     * @param fromIndex 开始查找的位置
     * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathLsatIndex(String fileName, int fromIndex) {
        int point = fileName.lastIndexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.lastIndexOf('\\', fromIndex);
        }
        return point;
    }
    /**
     * 将文件名中的类型部分去掉。
     *
     * @param filename 文件名
     * @return 去掉类型部分的结果
     * @since 1.0
     */
    public static String trimType(String filename) {
        int index = filename.lastIndexOf(".");
        if (index != -1) {
            return filename.substring(0, index);
        } else {
            return filename;
        }
    }

    /**
     * 得到相对路径。
     * 文件名不是目录名的子节点时返回文件名。
     *
     * @param pathName 目录名
     * @param fileName 文件名
     * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名
     * @since 1.0
     */
    public static String getSubPath(String pathName, String fileName) {
        int index = fileName.indexOf(pathName);
        if (index != -1) {
            return fileName.substring(index + pathName.length() + 1);
        } else {
            return fileName;
        }
    }

    /**
     * 检查给定目录的存在性
     * 保证指定的路径可用，如果指定的路径不存在，那么建立该路径，可以为多级路径
     *
     * @param path 路径
     * @return 真假值
     * @since 1.0
     */
    public static final boolean pathValidate(String path) {
        //String path="d:/web/www/sub";
        //System.out.println(path);
        //path = getUNIXfilePath(path);
        //path = ereg_replace("^\\/+", "", path);
        //path = ereg_replace("\\/+$", "", path);
        String[] array = path.split("/");
        StringBuilder builder = new StringBuilder();
        for (String s : array) {
            builder.append("/").append(s);
            File d = new File(builder.substring(1));
            if (!d.exists()) { //检查Sub目录是否存在
                System.out.println(builder.substring(1));
                if (!d.mkdir()) {
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * 读取文件的内容
     * 读取指定文件的内容
     *
     * @param path 为要读取文件的绝对路径
     * @return 以行读取文件后的内容。
     * @since 1.0
     */
    public static String getFileContent(String path) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        File f = new File(path);
        if (f.exists()) {
            FileReader fr = new FileReader(path);
            //建立BufferedReader对象，并实例化为br
            BufferedReader br = new BufferedReader(fr);
            //从文件读取一行字符串
            String line = br.readLine();
            //判断读取到的字符串是否不为空
            while (line != null) {
                stringBuilder.append(line).append("\n");
                line = br.readLine(); //从文件中继续读取一行数据
            }
            br.close(); //关闭BufferedReader对象
            fr.close(); //关闭文件
        }
        return stringBuilder.toString();
    }

    /**
     * 根据内容生成文件
     *
     * @param path          要生成文件的绝对路径，
     * @param content 文件的内容。
     * @return 真假值
     * @since 1.0
     */
    public static boolean genModuleTpl(String path, String content) throws IOException {
        path = getUNIXFilePath(path);
        String[] array = path.split("/");
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < array.length - 1; i++) {
            builder.append("/").append(array[i]);
        }
        File d = new File(builder.substring(1));
        if (!d.exists() && !pathValidate(builder.substring(1))) {
            return false;
        }
        //建立FileWriter对象，并实例化fw
        FileWriter fw = new FileWriter(path);
        //将字符串写入文件
        fw.write(content);
        fw.close();
        return true;
    }

    /**
     * 获取图片文件的扩展名（发布系统专用）
     *
     * @param picPath 为图片名称加上前面的路径不包括扩展名
     * @return 图片的扩展名
     * @since 1.0
     */
    public static String getPicExtendName(String picPath) {
        picPath = getUNIXFilePath(picPath);
        if (isFileExist(picPath + ".gif")) {
            return ".gif";
        }
        if (isFileExist(picPath + ".jpeg")) {
            return ".jpeg";
        }
        if (isFileExist(picPath + ".jpg")) {
            return".jpg";
        }
        if (isFileExist(picPath + ".png")) {
            return".png";
        }
        //返回图片扩展名
        return "";
    }

    /**
     * 拷贝文件
     *
     * @param in  输入文件
     * @param out 输出文件
     * @return 是否成功
     */
    public static boolean copyFile(File in, File out) {
        try {
            FileInputStream fis = new FileInputStream(in);
            FileOutputStream fos = new FileOutputStream(out);
            byte[] buf = new byte[1024];
            int i;
            while ((i = fis.read(buf)) != -1) { fos.write(buf, 0, i); }
            fis.close();
            fos.close();
            return true;
        } catch (IOException ie) {
            ie.printStackTrace();
            return false;
        }
    }

    /**
     * 拷贝文件
     *
     * @param infile  输入字符串
     * @param outfile 输出字符串
     * @return 是否成功
     */
    public static boolean copyFile(String infile, String outfile) {
        File in = new File(infile);
        File out = new File(outfile);
        return copyFile(in, out);

    }

    /**
     * 把内容content写的path文件中
     *
     * @param content 输入内容
     * @param path 文件路径
     * @return 是否成功
     */
    public static boolean saveFileAs(String content, String path) {
        try (FileWriter fw =new FileWriter(new File(path), false)){
            if (content != null) {
                fw.write(content);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 以字节为单位读取文件，常用于读二进制文件，如图片、声音、影像等文件。
     */
    public static byte[] readFileByBytes(String fileName) throws IOException {
        byte[] bytes = null;
        try (FileInputStream in = new FileInputStream(fileName)){
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            byte[] temp = new byte[1024];
            int size;
            while ((size = in.read(temp)) != -1) { out.write(temp, 0, size); }
            bytes = out.toByteArray();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return bytes;
    }
    /**
     * 以字符为单位读取文件，常用于读文本，数字等类型的文件
     */
    public static void readFileByChars(String fileName) {
        File file = new File(fileName);
        Reader reader = null;
        try {
            System.out.println("以字符为单位读取文件内容，一次读一个字节：");
            // 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file));
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                // 对于windows下，\r\n这两个字符在一起时，表示一个换行。
                // 但如果这两个字符分开显示时，会换两次行。
                // 因此，屏蔽掉\r，或者屏蔽\n。否则，将会多出很多空行。
                if (((char) tempchar) != '\r') {
                    System.out.print((char) tempchar);
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("以字符为单位读取文件内容，一次读多个字节：");
            // 一次读多个字符
            char[] chars = new char[30];
            int charRead;
            reader = new InputStreamReader(new FileInputStream(fileName));
            // 读入多个字符到字符数组中，charRead为一次读取字符数
            while ((charRead = reader.read(chars)) != -1) {
                // 同样屏蔽掉\r不显示
                if ((charRead == chars.length) && (chars[chars.length - 1] != '\r')) {
                    System.out.print(chars);
                } else {
                    for (int i = 0; i < charRead; i++) {
                        if (chars[i] == '\r') {
                            continue;
                        } else {
                            System.out.print(chars[i]);
                        }
                    }
                }
            }

        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * 以行为单位读取文件，常用于读面向行的格式化文件
     */
    public static List<String> readFileByLines(String fileName) {
        List<String> returnString = new ArrayList<>();
        File file = new File(fileName);
        try (BufferedReader reader = new BufferedReader(new FileReader(file));){
            String tempString;
            // 一次读入一行，直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                // 显示行号
                returnString.add(tempString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return returnString;
    }

    /**
     * 随机读取文件内容
     */
    public static void readFileByRandomAccess(String fileName) {
        // 打开一个随机访问文件流，按只读方式
        try (RandomAccessFile randomFile = new RandomAccessFile(fileName, "r")){
            System.out.println("随机读取一段文件内容：");
            // 文件长度，字节数
            long fileLength = randomFile.length();
            // 读文件的起始位置
            int beginIndex = (fileLength > 4) ? 4 : 0;
            // 将读文件的开始位置移到beginIndex位置。
            randomFile.seek(beginIndex);
            byte[] bytes = new byte[10];
            int byteRead;
            // 一次读10个字节，如果文件内容不足10个字节，则读剩下的字节。
            // 将一次读取的字节数赋给byteRead
            while ((byteRead = randomFile.read(bytes)) != -1) {
                System.out.write(bytes, 0, byteRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 读取文件内容
     *
     * @param fileName 文件
     * @return 内容
     */
    public static String readFileAll(String fileName) {
        String encoding = "UTF-8";
        File file = new File(fileName);
        long length = file.length();
        byte[] fileContent = new byte[(int) length];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(fileContent);
            in.close();
            return new String(fileContent, encoding);
        } catch (IOException e) {
            System.err.println("The OS does not support " + encoding);
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        System.out.println(joinPathByName("."));
        System.out.println(joinPathByName(".","a","test2.txt"));
        System.out.println(joinPathByName("..","b","test3.txt"));
        System.out.println(joinPathByName("/","c","test4.txt"));
        System.out.println(joinPathByName("\\","d","test5.txt"));
        System.out.println(joinPathByName("a","d","test6.txt"));
        System.out.println(joinPathByName("C","d","test7.txt"));
        System.out.println(getProjectRootPath());
    }
}